package com.bringspring.oauth.config;

import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.bringspring.common.base.ActionResult;
import com.bringspring.oauth.config.jwt.JwtTokenEnhancer;
import com.bringspring.oauth.config.password.MyPasswordEncoder;
import com.bringspring.oauth.method.detail.UserDetailsServiceImpl;
import com.bringspring.oauth.method.dingding.granter.DingTalkAbstractTokenGranter;
import com.bringspring.oauth.method.dingdingH5.granter.DingTalkH5AbstractTokenGranter;
import com.bringspring.oauth.method.password.granter.AccountPasswordAbstractTokenGranter;
import com.bringspring.oauth.method.qywechat.granter.WeComAbstractTokenGranter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author 开发平台组
 * @version V1.0.0
 * @copyright 股份有限公司
 * @date 2021/3/16
 */
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private AuthenticationManager authenticationManagers;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private MyPasswordEncoder myPasswordEncoder;

    /**
     * 配置允许的客户端
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(myPasswordEncoder);
        clients.withClientDetails(jdbcClientDetailsService);
    }

    /**
     * 允许表单验证
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
    }


    /**
     * 自定义认证异常响应数据
     *
     * @return
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, e) -> {
            response.setStatus(HttpStatus.HTTP_OK);
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Cache-Control", "no-cache");
            ActionResult result = ActionResult.fail("客户端认证失败");
            response.getWriter().print(JSONUtil.toJsonStr(result));
            response.getWriter().flush();
        };
    }

    @Autowired
    private AuthorizationServerTokenServices tokenServices;


    public CompositeTokenGranter getTokenGrater(AuthorizationServerEndpointsConfigurer endpoints) {

        ClientDetailsService clientDetailsService = endpoints.getClientDetailsService();
        endpoints.setClientDetailsService(clientDetailsService);

        OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
        // 默认tokenGranter集合
        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        // 添加授权码模式
        granters.add(new AuthorizationCodeTokenGranter(tokenServices, endpoints.getAuthorizationCodeServices(), endpoints.getClientDetailsService(), requestFactory));
        // 添加刷新令牌的模式
        granters.add(new RefreshTokenGranter(tokenServices, endpoints.getClientDetailsService(), requestFactory));
        // 添加隐士授权模式
        granters.add(new ImplicitTokenGranter(tokenServices, endpoints.getClientDetailsService(), requestFactory));
        // 添加客户端模式
        granters.add(new ClientCredentialsTokenGranter(tokenServices, endpoints.getClientDetailsService(), requestFactory));
        granters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, endpoints.getClientDetailsService(), requestFactory));
        // 增加企业微信Code登录方式
        granters.add(new WeComAbstractTokenGranter(userDetailsService, authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
        // 增加钉钉登录
        granters.add(new DingTalkAbstractTokenGranter(userDetailsService, authenticationManagers, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));
        // 增加钉钉H5登录
        granters.add(new DingTalkH5AbstractTokenGranter(userDetailsService, authenticationManagers, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));

        // 自定义账户密码登录
        granters.add(new AccountPasswordAbstractTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));

        // 组合tokenGranter集合
        return new CompositeTokenGranter(granters);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        CompositeTokenGranter tokenGrater = this.getTokenGrater(endpoints);
        List<TokenEnhancer> tokenEnhancer = new ArrayList<>();
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancer);
        //配置Jwt内容填充器
        tokenEnhancer.add(jwtTokenEnhancer);
        tokenEnhancer.add(jwtAccessTokenConverter);
        endpoints.authenticationManager(authenticationManager).tokenGranter(tokenGrater).userDetailsService(userDetailsService).accessTokenConverter(jwtAccessTokenConverter).tokenEnhancer(tokenEnhancerChain).pathMapping("/oauth/token", "/api/oauth/Login").tokenStore(tokenStore)
                //不可重复使用
                .reuseRefreshTokens(false);
    }


}
